﻿/* ================================================================================
<copyright file="LogSubscriber.cs" company="The Framework Factory, LLC" lastWriteTime="12/05/2012 23:23:47 PM">
  Copyright (c) 2012 The Framework Factory, LLC.  All rights reserved.
</copyright>
=================================================================================== */
using System;
using System.Runtime.Remoting;

namespace FWF.Logging.Net
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class LogSubscriber : MarshalByRefObject, ILogSubscriber, IDisposable
    {
        private static ILogPublisher _publisher;
        private static Uri _publisherUrl;
        private static Uri _listenUrl;
        private static bool _isConnected;

        private static readonly IFWFLog _log = LogFactory.Get( typeof ( LogSubscriber ) );

        #region Constructors / Finalizer

        /// <summary>
        /// Initializes a new instance of the <see cref="LogSubscriber"/> class.
        /// </summary>
        public LogSubscriber()
        {
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="LogSubscriber"/> is reclaimed by garbage collection.
        /// </summary>
        ~LogSubscriber()
        {
            Dispose( false );
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether this instance is connected.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is connected; otherwise, <c>false</c>.
        /// </value>
        public static bool IsConnected
        {
            get
            {
                return _isConnected;
            }
        }

        /// <summary>
        /// Gets the listen URL.
        /// </summary>
        /// <value>
        /// The listen URL.
        /// </value>
        public static Uri ListenUrl
        {
            get
            {
                return _listenUrl;
            }
        }

        /// <summary>
        /// Gets the publisher URL.
        /// </summary>
        /// <value>
        /// The publisher URL.
        /// </value>
        public static Uri PublisherUrl
        {
            get
            {
                return _publisherUrl;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose( true );

            GC.SuppressFinalize( this );
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose( bool disposing )
        {
            Disconnect();
        }

        #endregion

        #region ITestRemoteCall Members

        /// <summary>
        /// Pings this instance.
        /// </summary>
        /// <returns></returns>
        public bool Ping()
        {
            return true;
        }

        #endregion

        #region ILogSubscriber Members

        /// <summary>
        /// Called when [receive message].
        /// </summary>
        /// <param name="timeStamp">The time stamp.</param>
        /// <param name="machineName">Name of the machine.</param>
        /// <param name="instanceName">Name of the instance.</param>
        /// <param name="loggerName">Name of the logger.</param>
        /// <param name="levelValue">The level value.</param>
        /// <param name="levelName">Name of the level.</param>
        /// <param name="className">Name of the class.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="lineNumber">The line number.</param>
        /// <param name="methodName">Name of the method.</param>
        /// <param name="sessionId">The session id.</param>
        /// <param name="sessionName">Name of the session.</param>
        /// <param name="sessionContext">The session context.</param>
        /// <param name="message">The message.</param>
        /// <param name="exceptionType">Type of the exception.</param>
        /// <param name="exceptionMessage">The exception message.</param>
        /// <param name="threadName">Name of the thread.</param>
        /// <param name="userName">Name of the user.</param>
        public void LogEvent(
            DateTime timeStamp, 
            string machineName, 
            string instanceName, 
            string loggerName, 
            int levelValue, 
            string levelName, 
            string className, 
            string fileName, 
            int lineNumber, 
            string methodName, 
            int sessionId, 
            string sessionName, 
            string sessionContext, 
            string message, 
            string exceptionType, 
            string exceptionMessage, 
            string threadName, 
            string userName
            )
        {
            if (string.IsNullOrEmpty(message))
            {
                return;
            }

            var itm = new LogEventItem
                {
                    TimeStamp = timeStamp,
                    MachineName = machineName,
                    InstanceName = instanceName,
                    LoggerName = loggerName,
                    LevelValue = levelValue,
                    LevelName = levelName,
                    ClassName = className,
                    FileName = fileName,
                    LineNumber = lineNumber,
                    MethodName = methodName,
                    SessionId = sessionId,
                    SessionName = sessionName,
                    SessionContext = sessionContext,
                    Message = message,
                    ExceptionType = exceptionType,
                    ExceptionMessage = exceptionMessage,
                    ThreadName = threadName,
                    UserName = userName,
                };

            //
            _log.Log( itm.ToLoggingEventData() );
        }

        /// <summary>
        /// Closes this instance.
        /// </summary>
        public void Close()
        {
            Disconnect();
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        public static int RegisterAndListen()
        {
            //
            _listenUrl = PortLib.RegisterNextAvailableChannel( "LogSubscriber" );

            //
            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof( LogSubscriber ),
                "LogSubscriber",
                WellKnownObjectMode.Singleton
                );

            return _listenUrl.Port;
        }

        /// <summary>
        /// Connects this instance.
        /// </summary>
        public static void Connect( Uri remoteUrl )
        {
            // If already connected, ignore
            if ( IsConnected )
            {
                return;
            }

            // Connect to the remote application
            _publisher = (ILogPublisher) RemotingServices.Connect(
                typeof( ILogPublisher ),
                remoteUrl.ToString()
                );

            // Call the remote application to ensure remote communication
            bool bSuccess = _publisher.Ping();

            if ( !bSuccess )
            {
                throw new Exception( "Unable to ping remoting location" );
            }

            //
            _publisherUrl = remoteUrl;

            // Add a reference to this component so that we can receive callbacks on the IRemoteMessageObserverEvents contract
            _publisher.AddSubscription( _listenUrl );

            ////
            //_publisher.AddSubscriptionDetail( 
            //    _listenUrl,
            //    "FWF.Logging.ConsoleTestApp.Program", 
            //    0 
            //    );

            // If successful, then register the type
            //WellKnownClientTypeEntry[] entries = RemotingConfiguration.GetRegisteredWellKnownClientTypes();

            ////
            //if ( !Array.Exists(
            //    entries,
            //    x => x.ObjectType == typeof( ILogPublisher )
            //    )
            //    )
            //{
            //    RemotingConfiguration.RegisterWellKnownClientType(
            //        typeof( ILogPublisher ),
            //        publisherUrl
            //        );
            //}

            //// Get the remote ILogPublisher contract
            //WellKnownClientTypeEntry clientSubscriptionProxyEntry = null;
            //foreach ( var registeredWellKnownClientType in RemotingConfiguration.GetRegisteredWellKnownClientTypes() )
            //{
            //    if ( registeredWellKnownClientType.ObjectType == typeof ( ILogPublisher ) )
            //    {
            //        clientSubscriptionProxyEntry = registeredWellKnownClientType;
            //        break;
            //    }
            //}

            ////
            //try
            //{
            //    // Connect to the remote application
            //    _publisher = (LogPublisher) RemotingServices.Connect(
            //        clientSubscriptionProxyEntry.ObjectType,
            //        clientSubscriptionProxyEntry.ObjectUrl
            //        );

            //    //
            //    _publisher.Ping();
            //}
            //catch ( Exception )
            //{
            //    // TODO: log, show dialog

            //    return;
            //}


            //
            _isConnected = true;
        }

        /// <summary>
        /// Disconnects this instance.
        /// </summary>
        public static void Disconnect()
        {
            // If already disconnected, ignore
            if ( !_isConnected )
            {
                return;
            }
            if ( _publisher == null )
            {
                return;
            }

            //
            _isConnected = false;

            //
            try
            {
                _publisher.RemoveSubscription( _listenUrl );
            }
            catch ( Exception )
            {
                // Ignore, contiue to try to disconnect
            }
        }

    }
}
